package cn.com.idmy.auth.session;

import cn.com.idmy.auth.Device;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Data
@Accessors(fluent = true)
@EqualsAndHashCode(of = {"accountType", "id"})
@NoArgsConstructor
public class Session<ID> {
    protected long accountType;
    @NotNull
    protected ID id;
    @NotNull
    protected LocalDateTime expAt;
    @NotNull
    protected LocalDateTime createdAt;
    @NotNull
    protected Map<String, Token> tokens = new ConcurrentHashMap<>();
    @NotNull
    protected Map<String, Object> data = new ConcurrentHashMap<>();
    @NotNull
    @JSONField(serialize = false, deserialize = false)
    protected SessionManager<ID> sessionManager;

    public Session(@NotNull ID id) {
        this.id = id;
        this.createdAt = LocalDateTime.now();
    }

    public void update() {
        sessionManager.update(this);
    }

    public boolean hasToken(@NotNull String token) {
        return tokens.containsKey(token);
    }

    @Nullable
    public Token getToken(@NotNull String token) {
        return tokens.get(token);
    }

    @Nullable
    public String getToken(@NotNull Device device) {
        for (var entry : tokens.entrySet()) {
            if (entry.getValue().device().equals(device)) {
                return entry.getKey();
            }
        }
        return null;
    }

    public void addToken(@NotNull String token, @NotNull Device device, @Nullable JSONObject data) {
        tokens.put(token, new Token(device, data));
    }

    public void removeToken(@NotNull String token) {
        tokens.remove(token);
    }

    @Nullable
    public JSONObject getTokenData(@NotNull String token) {
        Token t = getToken(token);
        if (t == null) {
            return null;
        } else {
            return t.data();
        }
    }

    @Nullable
    public Device getTokenDevice(@NotNull String token) {
        Token t = getToken(token);
        if (t == null) {
            return null;
        } else {
            return t.device();
        }
    }

    @Nullable
    public Object get(@NotNull String key) {
        return data.get(key);
    }

    public void set(@NotNull String key, @NotNull Object value) {
        data.put(key, value);
    }

    public void remove(@NotNull String key) {
        data.remove(key);
    }

    @NotNull
    public Set<String> keys() {
        return data.keySet();
    }

    public void clear() {
        data.clear();
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }

    public record Token(Device device, JSONObject data) {
    }
}
